package com.xiaoshuidi.cloud.module.finance.service.finance.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xiaoshuidi.cloud.framework.common.core.KeyValue;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.framework.yinlian.core.trade.PullTransactionFlowService;
import com.xiaoshuidi.cloud.framework.yinlian.core.trade.YinlianExcelConsumer;
import com.xiaoshuidi.cloud.framework.yinlian.core.trade.vo.YinlianTransactionExcelVO;
import com.xiaoshuidi.cloud.module.finance.convert.finance.YinlianConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceReconciliationData;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceReconciliationLogDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.YinlianReconciliationDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceReconciliationLogMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.YinlianReconciliationMapper;
import com.xiaoshuidi.cloud.module.finance.service.finance.YinlianReconciliationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 安徽银联对账
 */
@Slf4j
@Service("anhuiYinlianReconciliationService")
public class YinlianReconciliationServiceImpl implements YinlianReconciliationService {
    @Resource
    private PullTransactionFlowService pullTransactionFlowService;
    @Resource
    private YinlianReconciliationMapper yinlianReconciliationMapper;
    @Resource
    private FinanceReconciliationLogMapper financeReconciliationLogMapper;

    @Override
    public void reconciliation() {
        // 需要对账的商户号和日期(T+1)
        List<KeyValue<String, LocalDate>> l = TenantUtils.executeIgnoreAndReturn(() ->
                yinlianReconciliationMapper.selectMerchantAndDate());
        for (KeyValue<String, LocalDate> kv : l) {
            log.info("银联商户 {}/{} 开始对账", kv.getKey(), kv.getValue());
            pull(kv.getKey(), kv.getValue().toString());
        }
    }

    /**
     * 拉取指定商户号结算日期的对账文件并对账
     *
     * @param merchantCode
     * @param date
     */
    @Override
    public void pull(String merchantCode, String date) {
        // 获取商户号下配置的所有租户id
        Set<Long> tenants = TenantUtils.executeIgnoreAndReturn(() -> {
            // 删除之前对应商户号及日期下的对账数据
            yinlianReconciliationMapper.deleteByMerchantCodeAndSettleDate(merchantCode, date);
            return yinlianReconciliationMapper.selectTenantIdByMerchantCode(merchantCode);
        });
        YinlianExcelConsumer<YinlianTransactionExcelVO> yinlianExcelConsumer =
                new YinlianExcelConsumer<YinlianTransactionExcelVO>() {
                    String fileName;
                    List<YinlianTransactionExcelVO> data = new ArrayList<>();
                    Runnable onComplete;
                    int count = 0;

                    boolean completed;

                    @Override
                    public void accept(YinlianTransactionExcelVO excelVO) {
                        if (excelVO.getTotalAmount() == null) {
                            if (!data.isEmpty()) {
                                save(data);
                                data.clear();
                            }
                            if (completed) {
                                return;
                            }
                            if (count > 0 && onComplete != null) {
                                log.info("银联对账[{}][{}]文件:[{}]总计: {}条", merchantCode, date, fileName, count);
                                completed = true;
                                onComplete.run();
                            } else if (count == 0) {
                                log.info("银联对账[{}][{}]文件:[{}]没有交易数据", merchantCode, date, fileName);
                            }
                            return;
                        }
                        count++;
                        data.add(excelVO);
                        // 批量落库
                        if (data.size() >= 500) {
                            save(data);
                            data.clear();
                        }
                    }

                    public void save(List<YinlianTransactionExcelVO> data) {
                        List<YinlianReconciliationDO> l = YinlianConvert.INSTANCE.convert(data, merchantCode);
                        if (!CollectionUtils.isEmpty(l)) {
                            log.info("解析{}行记录, 共插入{}条记录", data.size(), l.size());
                            yinlianReconciliationMapper.insertBatch(l);
                        }
                    }

                    @Override
                    public String getFileName() {
                        return fileName;
                    }

                    @Override
                    public void setFileName(String file) {
                        this.fileName = file;
                    }

                    @Override
                    public void onComplete(Runnable runnable) {
                        this.onComplete = runnable;
                    }

                };
        // 文件读取完成
        yinlianExcelConsumer.onComplete(() -> {
            // 标记对账失败数据
            TenantUtils.executeIgnore(() -> yinlianReconciliationMapper.markNotMatchData(merchantCode, date));
            for (Long tenant : tenants) {
                TenantUtils.executeIgnore(() -> // 标记对账成功数据
                        yinlianReconciliationMapper.markMatchData(merchantCode, date, tenant));

                TenantUtils.execute(tenant, () -> {
                    // 删除之前的数据
                    financeReconciliationLogMapper.delete(new QueryWrapper<FinanceReconciliationLogDO>()
                            .eq("pay_channel", "银联")
                            .eq("settlement_date", date)
                    );
                    // 获取对账汇总数据
                    // 左边是系统数据
                    FinanceReconciliationData left = yinlianReconciliationMapper.selectLeftSummary(merchantCode, date, tenant);
                    // 右边是银联数据
                    FinanceReconciliationData right = yinlianReconciliationMapper.selectRightSummary(merchantCode, date, tenant);
                    // 保存对账数据
                    financeReconciliationLogMapper.insert(FinanceReconciliationLogDO.builder()
                            .payChannel("银联")
                            .settlementDate(LocalDate.parse(date))
                            .totalAmount(left.getAmount())
                            .totalCount(left.getTotal())
                            .channelTotalAmount(right.getAmount())
                            .channelTotalCount(right.getTotal())
                            .totalServiceCharge(right.getServiceFee())
                            .success(left.equalsTo(right))
                            .settlementFile(yinlianExcelConsumer.getFileName())
                            .createTime(LocalDateTime.now())
                            .build()
                    );
                });
            }
        });

        try {
            pullTransactionFlowService.pull(merchantCode, LocalDate.parse(date), yinlianExcelConsumer);
        } catch (IOException e) {
            throw new ServiceException(500, e.getMessage());
        } catch (Exception e) {
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
        }
    }
}
